package com.zqh.apiordersubmit.config;

/**
 * @Description : 通过aop手动开启seata事务组并判断是否需要回滚事务组，解决全局异常处理及feign降级导致分布式事务回滚失败
 * @Author :kono淇杭哒、
 * @Create 2022-03-19 15:55
 */

import com.alibaba.cloud.commons.lang.StringUtils;
import com.zqh.myfmmall.result.Result;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Slf4j
@Component
public class WorkAspect {


    /**
     * 功能描述:
     * 监听目标包下所有具体实现方法（分布式全局事务的入口方法），在入口方法执行前开启一个全局事务
     *
     * @param joinPoint
     * @return void
     * @author kono曾淇杭哒！
     * @date 2022/3/19 21:29
     */
    @Before("execution(* com.zqh.apiordersubmit.service.*.*(..))")
    public void before(JoinPoint joinPoint) throws TransactionException {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        log.info("拦截到需要分布式事务的方法," + method.getName());
        // 此处可用redis或者定时任务来获取一个key判断是否需要关闭分布式事务
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
        try {
            tx.begin(300000, "test-client");
        } catch (TransactionException e) {
            e.printStackTrace();
        }
        log.info("创建分布式事务完毕" + tx.getXid());
    }

    /**
     * 功能描述:
     *  监听目标包下所有具体实现方法（分布式全局事务的入口方法），当捕捉到异常时，则对全局事务进行回滚
     *  这个方法可能不能正常触发然后执行，因为抛出的异常可能已经被其他捕捉了，
     *  可以通过在feign的降级逻辑中手动执行该方法，但是也有限制即一个全局事务中只能有一个feign调用触发降级然后触发回滚
     *  如果又有第二个feign调用触发降级然后执行回滚，因为已经回滚过了，就会导致报错
     *
     *  可以不使用这个，就通过使用下面监听方法返回来实现
     * @param
     * @return void
     * @author kono曾淇杭哒！
     * @date 2022/3/19 21:32
     */
    @AfterThrowing(throwing = "e", pointcut = "execution(* com.zqh.apiordersubmit.service.*.*(..))")
    public void doRecoveryActions(Throwable e) throws TransactionException {
        log.info("分布式事务方法执行异常:{}", e.getMessage());
        if (!StringUtils.isBlank(RootContext.getXID())) {
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
        }
    }

    /**
     * 功能描述:
     *  监听目标包下所有具体实现方法（分布式全局事务的入口方法），当方法执行return后根据返回结果判断是否要进行全局事务回滚
     *
     * @param
     * @return void
     * @author kono曾淇杭哒！
     * @date 2022/3/19 21:32
     */
    @AfterReturning(value = "execution(* com.zqh.apiordersubmit.service.*.*(..))", returning = "result")
    public void afterReturning(JoinPoint point, Object result) throws TransactionException {
        log.info("方法执行结束:{}", result);
        if(result instanceof Result){
            Result resu = (Result) result;
            if (!resu.getSuccess()) {
                if (!StringUtils.isBlank(RootContext.getXID())) {
                    log.info("回滚分布式事务Id:{}", RootContext.getXID());
                    GlobalTransactionContext.reload(RootContext.getXID()).rollback();
                }
            }
        }else if (result instanceof Boolean){
            if (!(Boolean)result) {
                if (!StringUtils.isBlank(RootContext.getXID())) {
                    log.info("回滚分布式事务Id:{}", RootContext.getXID());
                    GlobalTransactionContext.reload(RootContext.getXID()).rollback();
                }
            }
        }else if (result == null){
            if (!StringUtils.isBlank(RootContext.getXID())) {
                log.info("回滚分布式事务Id:{}", RootContext.getXID());
                GlobalTransactionContext.reload(RootContext.getXID()).rollback();
            }
        }
    }

}

